View Javadoc

1   package org.apache.maven.surefire.junitcore.pc;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.surefire.testset.TestSetFailedException;
23  import org.junit.runner.Computer;
24  import org.junit.runner.Description;
25  
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.TreeSet;
29  import java.util.concurrent.Callable;
30  import java.util.concurrent.Executors;
31  import java.util.concurrent.Future;
32  import java.util.concurrent.ScheduledExecutorService;
33  
34  import static java.util.concurrent.TimeUnit.*;
35  
36  /**
37   * ParallelComputer extends JUnit {@link Computer} and has a shutdown functionality.
38   *
39   * @author Tibor Digana (tibor17)
40   * @see ParallelComputerBuilder
41   * @since 2.16
42   */
43  public abstract class ParallelComputer
44      extends Computer
45  {
46      private final long timeoutNanos;
47  
48      private final long timeoutForcedNanos;
49  
50      private ScheduledExecutorService shutdownScheduler;
51  
52      private Future<Collection<Description>> testsBeforeShutdown;
53  
54      private Future<Collection<Description>> testsBeforeForcedShutdown;
55  
56      public ParallelComputer( double timeoutInSeconds, double timeoutForcedInSeconds  )
57      {
58          this.timeoutNanos = secondsToNanos( timeoutInSeconds );
59          this.timeoutForcedNanos = secondsToNanos( timeoutForcedInSeconds );
60      }
61  
62      private static long secondsToNanos( double seconds )
63      {
64          double nanos = seconds > 0 ? seconds * 1E9 : 0;
65          return Double.isInfinite( nanos ) || nanos >= Long.MAX_VALUE ? 0 : (long) nanos;
66      }
67  
68      private static long minTimeout( long timeout1, long timeout2 )
69      {
70          if ( timeout1 == 0 )
71          {
72              return timeout2;
73          }
74          else if ( timeout2 == 0 )
75          {
76              return timeout1;
77          }
78          else
79          {
80              return Math.min( timeout1, timeout2 );
81          }
82      }
83  
84      private static Collection<String> printShutdownHook( Future<Collection<Description>> future )
85          throws TestSetFailedException
86      {
87          if ( !future.isCancelled() && future.isDone() )
88          {
89              try
90              {
91                  TreeSet<String> executedTests = new TreeSet<String>();
92                  for ( Description executedTest : future.get() )
93                  {
94                      if ( executedTest != null && executedTest.getDisplayName() != null )
95                      {
96                          executedTests.add( executedTest.getDisplayName() );
97                      }
98                  }
99                  return executedTests;
100             }
101             catch ( Exception e )
102             {
103                 throw new TestSetFailedException( e );
104             }
105         }
106         return Collections.emptySet();
107     }
108 
109     public abstract Collection<Description> shutdown( boolean shutdownNow );
110 
111     protected final void beforeRunQuietly()
112     {
113         testsBeforeShutdown = timeoutNanos > 0 ? scheduleShutdown() : null;
114         testsBeforeForcedShutdown = timeoutForcedNanos > 0 ? scheduleForcedShutdown() : null;
115     }
116 
117     protected final void afterRunQuietly()
118     {
119         if ( shutdownScheduler != null )
120         {
121             shutdownScheduler.shutdownNow();
122         }
123     }
124 
125     public String describeElapsedTimeout()
126         throws TestSetFailedException
127     {
128         TreeSet<String> executedTests = new TreeSet<String>();
129         if ( testsBeforeShutdown != null )
130         {
131             executedTests.addAll( printShutdownHook( testsBeforeShutdown ) );
132         }
133 
134         if ( testsBeforeForcedShutdown != null )
135         {
136             executedTests.addAll( printShutdownHook( testsBeforeForcedShutdown ) );
137         }
138 
139         StringBuilder msg = new StringBuilder();
140         if ( !executedTests.isEmpty() )
141         {
142             msg.append( "The test run has finished abruptly after timeout of " );
143             msg.append( nanosToSeconds( minTimeout( timeoutNanos, timeoutForcedNanos ) ) );
144             msg.append( " seconds.\n" );
145             msg.append( "These tests were executed in prior of the shutdown operation:\n" );
146             for ( String executedTest : executedTests )
147             {
148                 msg.append( executedTest ).append( '\n' );
149             }
150         }
151         return msg.toString();
152     }
153 
154     private Future<Collection<Description>> scheduleShutdown()
155     {
156         return getShutdownScheduler().schedule( createShutdownTask( false ), timeoutNanos, NANOSECONDS );
157     }
158 
159     private Future<Collection<Description>> scheduleForcedShutdown()
160     {
161         return getShutdownScheduler().schedule( createShutdownTask( true ), timeoutForcedNanos, NANOSECONDS );
162     }
163 
164     private ScheduledExecutorService getShutdownScheduler()
165     {
166         if ( shutdownScheduler == null )
167         {
168             shutdownScheduler = Executors.newScheduledThreadPool( 2 );
169         }
170         return shutdownScheduler;
171     }
172 
173     private Callable<Collection<Description>> createShutdownTask( final boolean isForced )
174     {
175         return new Callable<Collection<Description>>()
176         {
177             public Collection<Description> call()
178                 throws Exception
179             {
180                 return ParallelComputer.this.shutdown( isForced );
181             }
182         };
183     }
184 
185     private double nanosToSeconds( long nanos )
186     {
187         return (double) nanos / 1E9;
188     }
189 }